package random;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

/**
 * 传递信息（dfs、bfs、动态规划）
 *
 * 小朋友 A 在和 ta 的小伙伴们玩传信息游戏，游戏规则如下：
 *
 * 有 n 名玩家，所有玩家编号分别为 0 ～ n-1，其中小朋友 A 的编号为 0
 * 每个玩家都有固定的若干个可传信息的其他玩家（也可能没有）。传信息的关系是单向的（比如 A 可以向 B 传信息，但 B 不能向 A 传信息）。
 * 每轮信息必须需要传递给另一个人，且信息可重复经过同一个人
 * 给定总玩家数 n，以及按 [玩家编号,对应可传递玩家编号] 关系组成的二维数组 relation。返回信息从小 A (编号 0 ) 经过 k 轮传递到编号为 n-1 的小伙伴处的方案数；
 * 若不能到达，返回 0。
 */

public class LCP7 {

    public static void main(String[] args) {

        LCP7 lcp7 = new LCP7();
        System.out.println(lcp7.numWays(5, new int[][]{{0, 2}, {2, 1}, {3, 4}, {2, 3}, {1, 4}, {2, 0}, {0, 4}}, 3));
    }


    /**
     * 深度优先搜索
     *
     * 可以使用深度优先搜索计算方案数。从节点 0 出发做深度优先搜索，每一步记录当前所在的节点以及经过的轮数，当经过 k 轮时，
     * 如果位于节点 n−1，则将方案数加 1。搜索结束之后，即可得到总的方案数。
     * 具体实现方面，可以对传信息的关系进行预处理，使用列表存储有向边的关系，
     * 即可在 O(1) 的时间内得到特定节点的相邻节点（即可以沿着有向边一步到达的节点）。
     */
    int ways, n, k;
   List<List<Integer>> edges;

   // n 是传递人数 k 是传递轮数
    public  int numWays(int n, int[][] relation, int k) {
        ways = 0;
        this.n = n;
        this.k = k;
        // 初始化 edges
        edges = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            edges.add(new ArrayList<>());
        }
        // 遍历二维数组 把传递人 和 被传递人 封装成类似 map 的结构
        for (int[] edge : relation) {
            int src = edge[0], dst = edge[1];
            edges.get(src).add(dst);
        }
        // index表示开始下标 steps表示第几轮
        dfs(0, 0);
        return ways;
    }

    public void dfs(int index, int steps) {
        if (steps == k) {
            if (index == n - 1) {
                ways++;
            }
            return;
        }
        List<Integer> list = edges.get(index);
        for (int nextIndex : list) {
            dfs(nextIndex, steps + 1);
        }
    }


    /**
     * 广度优先搜索(使用队列实现)
     * 从节点 0 出发做广度优先搜索，当遍历到 k 层时，如果位于节点 n−1，则将方案数加 1。搜索结束之后，即可得到总的方案数。
     */
    public int numWays1(int n, int[][] relation, int k) {
        // 初始化 edges
        List<List<Integer>> edges = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            edges.add(new ArrayList<>());
        }
        for (int[] edge : relation) {
            int src = edge[0], dst = edge[1];
            edges.get(src).add(dst);
        }

        int steps = 0;
        // 队列
        Queue<Integer> queue = new LinkedList<>();
        // 插入数据(尾部)
        queue.offer(0);
        while (!queue.isEmpty() && steps < k) {
            steps++;
            int size = queue.size();
            for (int i = 0; i < size; i++) {
                // 头部移除数据
                int index = queue.poll();
                List<Integer> list = edges.get(index);
                for (int nextIndex : list) {
                    queue.offer(nextIndex);
                }
            }
        }

        int ways = 0;
        if (steps == k) {
            while (!queue.isEmpty()) {
                if (queue.poll() == n - 1) {
                    ways++;
                }
            }
        }
        return ways;
    }


    /**
     * 动态规划
     */
    public int numWays2(int n, int[][] relation, int k) {
        int[][] dp = new int[k + 1][n];
        dp[0][0] = 1;
        for (int i = 0; i < k; i++) {
            for (int[] edge : relation) {
                int src = edge[0], dst = edge[1];
                dp[i + 1][dst] += dp[i][src];
            }
        }
        return dp[k][n - 1];
    }

    public int numWays3(int n, int[][] relation, int k) {
        int[] dp = new int[n];
        dp[0] = 1;
        for (int i = 0; i < k; i++) {
            int[] next = new int[n];
            for (int[] edge : relation) {
                int src = edge[0], dst = edge[1];
                next[dst] += dp[src];
            }
            dp = next;
        }
        return dp[n - 1];
    }


}
